/**
 * 
 */
package soa.security.dao.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;

import org.apache.commons.lang3.StringUtils;
import org.ldaptive.Connection;
import org.ldaptive.Response;
import org.ldaptive.SearchOperation;
import org.ldaptive.SearchRequest;
import org.ldaptive.SearchResult;
import org.ldaptive.control.PagedResultsControl;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.query.ContainerCriteria;
import org.springframework.ldap.query.LdapQuery;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.ldap.query.SearchScope;
import org.springframework.ldap.support.LdapNameBuilder;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.stereotype.Repository;

import soa.security.Constants;
import soa.security.dao.RegionDao;
import soa.security.domain.Account;
import soa.security.domain.Applsystem;
import soa.security.domain.Department;
import soa.security.domain.Group;
import soa.security.domain.Person;
import soa.security.domain.Position;
import soa.security.domain.Region;
import soa.security.domain.Role;

/**
 * @author Cre.Gu
 *
 */
@Repository
public class RegionDaoImpl extends SpringLdapDaoImpl<Region> implements RegionDao {

	@Override
	public Region findByDn(String dn) {
		Region o = ldapTemplate.findByDn(LdapNameBuilder.newInstance(dn).build(), Region.class);
		o.setBid(LdapUtils.getStringValue(o.getDn(), o.getDn().size() - 1));
		return o;
	}

	@Override
	public List<Region> allRegions(String baseDn, SearchScope searchScope) {
		return allRegions(baseDn, searchScope, null);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public Page<Region> allRegions(String parentDn, org.ldaptive.SearchScope searchScope, PageRequest pageRequest)
			throws Exception {
		List<Region> results = new ArrayList<Region>();
		Connection conn = null;
		try {
			conn = pool.getConnection();
			conn.open();

			SearchOperation search = new SearchOperation(conn);

			LdapNameBuilder builder = LdapNameBuilder.newInstance(ldaptiveSupport.getBaseDn());
			if (StringUtils.isNotBlank(parentDn))
				builder.add(parentDn);

			String dn = builder.build().toString();
			String filter = "(&(objectclass=zjtzsw-region))";

			SearchRequest request = new SearchRequest(dn, filter);
			request.setSearchScope(searchScope);
			request.setSizeLimit(0);

			Response<SearchResult> response = search.execute(request);
			int total = response.getResult().size();

			PagedResultsControl prc = new PagedResultsControl(pageRequest.getPageSize());
			String[] dns = null;

			byte[] cookie = null;
			for (int i = 0; i < pageRequest.getPageNumber() + 1; i++) {

				prc.setCookie(cookie);
				request.setControls(prc);

				response = search.execute(request);
				PagedResultsControl ctl = (PagedResultsControl) response.getControl(PagedResultsControl.OID);

				dns = response.getResult().getEntryDns();

				cookie = ctl.getCookie();
				if (cookie == null)
					break;
			}

			for (String b : dns) {
				results.add(findByDn(ldaptiveSupport.wrap4Spring(b)));
			}

			return new PageImpl(results, pageRequest, total);
		} catch (Exception e) {
			throw e;
		} finally {
			if (conn != null)
				conn.close();
		}

	}

	protected class RegionContextMapper implements ContextMapper<Region> {

		private String baseDn;

		public RegionContextMapper() {
		}

		public RegionContextMapper(String baseDn) {
			super();
			this.baseDn = baseDn;
		}

		@Override
		public Region mapFromContext(Object ctx) throws NamingException {
			DirContextAdapter context = (DirContextAdapter) ctx;
			Region region = findByDn(context.getDn().toString());
			region.setParentDn(baseDn);

			return region;
		}

		public String getBaseDn() {
			return baseDn;
		}

		public void setBaseDn(String baseDn) {
			this.baseDn = baseDn;
		}
	}

	public String findMaxNumber(final Class<?> clazz) {
		String objectclass = "zjtzsw-organization";
		String attributesToReturn = "bid";
		if (Person.class.equals(clazz)) {
			objectclass = "zjtzsw-person";
			attributesToReturn = "uid";
		} else if (Department.class.equals(clazz)) {
			objectclass = "zjtzsw-department";
		} else if (Position.class.equals(clazz)) {
			objectclass = "zjtzsw-position";
		} else if (Group.class.equals(clazz)) {
			objectclass = "zjtzsw-group";
		} else if (Role.class.equals(clazz)) {
			objectclass = "zjtzsw-role";
		} else if (Region.class.equals(clazz)) {
			objectclass = "zjtzsw-region";
		} else if (Applsystem.class.equals(clazz)) {
			objectclass = "zjtzsw-applsystem";
		} 


		LdapQueryBuilder queryBuilder = LdapQueryBuilder.query();
		LdapQuery query = queryBuilder.attributes(attributesToReturn).where("objectclass").is(objectclass);

		List<String> ids = ldapTemplate.search(query, new AttributesMapper<String>() {

			@Override
			public String mapFromAttributes(Attributes attributes) throws NamingException {
				String attrID = "bid";
				if (Person.class.equals(clazz) || Account.class.equals(clazz))
					attrID = "uid";

				String aString = (String) attributes.get(attrID).get();
				return aString;
			}
		});

		String[] idsArr = ids.toArray(new String[0]);

		Arrays.sort(idsArr, new Comparator<String>() {

			@Override
			public int compare(String o1, String o2) {
				return o2.compareToIgnoreCase(o1);
			}
		});

		if (idsArr.length == 0)
			return "0001";

		String maxString = idsArr[0];
		String numString = StringUtils.substring(maxString, 2);
		Integer numInteger = Integer.valueOf(numString);

		String maxNum = String.valueOf(numInteger + 1);

		maxNum = StringUtils.leftPad(maxNum, 4, "0");

		return maxNum;
	}

	@Override
	public void deleteAllEntries(Name dn) {
		ldapTemplate.unbind(dn, true);
	}

	@Override
	public List<Region> existCode(String code) {
		LdapQueryBuilder queryBuilder = LdapQueryBuilder.query();
		LdapQuery query = queryBuilder.searchScope(SearchScope.SUBTREE).where("objectclass").is("zjtzsw-region")
				.and(Constants.DELETED).not().is("TRUE").and("ccode").is(code);

		List<Region> region = ldapTemplate.search(query, new RegionContextMapper());
		return region;
	}

	@Override
	public List<Region> allRegions(String baseDn, SearchScope searchScope, Map<String, Object> params) {
		ContainerCriteria query = baseQuery(baseDn, searchScope);

		buildLikeCriteria(query, params, "name");
		buildLikeCriteria(query, params, "ccode");

		return ldapTemplate.search(query, new RegionContextMapper(baseDn));
	}

	@Override
	public String getObjectClass() {
		return "zjtzsw-region";
	}

	@Override
	public Name findDnById(String id) {
		try {
			LdapQuery query = LdapQueryBuilder.query().where("objectclass").is("zjtzsw-region").and("bid").is(id);

			DirContextAdapter context = (DirContextAdapter) ldapTemplate.searchForContext(query);

			return context.getDn();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public boolean exist(String regionId) {
		// TODO Auto-generated method stub
		return findDnById(regionId) != null;
	}

	@Override
	public Region findByPrimaryKey(String regionId) {
		// TODO Auto-generated method stub
		if (exist(regionId))
			return findByDn(findDnById(regionId).toString());

		return null;
	}
}
